package redis

import (
	"errors"
	"fmt"
	"gitee.com/shijun123456/kcgin"
	"gitee.com/shijun123456/kcgin/logs"
	"github.com/gomodule/redigo/redis"
	"io"
	"strings"
	"time"
)

var (
	RedisGlobMgr *RedisMgr = &RedisMgr{}
)

type RedisMgr struct {
	pool *redis.Pool
}

func init() {
	db, err := kcgin.AppConfig.Int("redis.db")
	if err != nil {
		db = 0
	}
	RedisInit(db, RedisGlobMgr)
}

func RedisInit(db int, redism *RedisMgr) {
	host := fmt.Sprintf("%s:%s", kcgin.AppConfig.String("redis.host"), kcgin.AppConfig.String("redis.port"))

	//最大空闲连接数
	maxIdle, err := kcgin.AppConfig.Int("redis.maxIdle")
	if err != nil {
		maxIdle = 50
	}
	//最大连接数
	maxActive, err := kcgin.AppConfig.Int("redis.maxActive")
	if err != nil {
		maxActive = 300
	}
	//空闲链接超时时间
	idleTimeout, err := kcgin.AppConfig.Int64("redis.idleTimeout")
	if err != nil {
		idleTimeout = 50
	}
	//如果超过最大连接，是报错，还是等待
	wait, err := kcgin.AppConfig.Bool("redis.wait")
	if err != nil {
		wait = true
	}
	timeout := time.Duration(idleTimeout) * time.Second
	//password
	pass := kcgin.AppConfig.String("redis.pass")

	redism.pool = &redis.Pool{
		MaxIdle:     maxIdle,
		MaxActive:   maxActive,
		IdleTimeout: timeout,
		Wait:        wait,
		Dial: func() (conn redis.Conn, e error) {
			con, err := redis.Dial("tcp", host,
				redis.DialPassword(pass),
				redis.DialDatabase(db),
				redis.DialConnectTimeout(2*time.Second),
				redis.DialReadTimeout(2*time.Second),
				redis.DialWriteTimeout(3*time.Second))
			if err != nil {
				return nil, err
			}
			return con, nil
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			//距离上次使用时间小于30秒 不检测链接状态
			if time.Since(t) < time.Second*30 {
				return nil
			}
			_, err := c.Do("PING")
			if err != nil {
				logs.Info("redis connect err: ", err)
			}
			return err

		},
	}

}

func (r *RedisMgr) GetConn() redis.Conn {
	return r.pool.Get()
}

// 向一个key[队列]的尾部添加一个元素
func (r *RedisMgr) Rpush(key string, data interface{}) error {
	conn := r.GetConn()
	if conn.Err() != nil {
		return conn.Err()
	}

	defer func() {
		conn.Flush()
		conn.Close()
	}()

	err := conn.Send("RPUSH", key, data)
	if err != nil {
		conn2, _ := r.RetyCreateTempConn(err)
		if conn2 != nil {
			err = conn2.Send("RPUSH", key, data)
			conn2.Flush()
			conn2.Close()
		}
	}

	return err

}

//向一个key[队列]的头部添加一个元素
func (r *RedisMgr) Lpush(key string, data interface{}) error {
	conn := r.GetConn()
	if conn.Err() != nil {
		return conn.Err()
	}

	defer func() {
		conn.Flush()
		conn.Close()
	}()
	err := conn.Send("LPUSH", key, data)

	if err != nil {
		conn2, _ := r.RetyCreateTempConn(err)
		if conn2 != nil {
			err = conn2.Send("LPUSH", key, data)
			conn2.Flush()
			conn2.Close()
		}
	}

	return err
}

//取出队列中第一个key取元素值
func (r *RedisMgr) Lpop(key string) (interface{}, error) {
	conn := r.GetConn()
	if conn.Err() != nil {
		return nil, conn.Err()
	}
	defer func() {
		conn.Close()
	}()
	res, err := conn.Do("LPOP", key)

	if err != nil {
		conn2, _ := r.RetyCreateTempConn(err)
		if conn2 != nil {
			res, err = conn2.Do("LPOP", key)
			conn2.Close()
		}
	}
	return res, err
}

//返回名称为key的list中start至end之间的元素（end为 -1 ，返回所有）
func (r *RedisMgr) Lrange(key string, start int, end int) (interface{}, error) {
	conn := r.GetConn()
	if conn.Err() != nil {
		return nil, conn.Err()
	}

	defer func() {
		conn.Close()
	}()
	res, err := conn.Do("LRANGE", key, start, end)
	if err != nil {
		conn2, _ := r.RetyCreateTempConn(err)
		if conn2 != nil {
			res, err = conn2.Do("LRANGE", key, start, end)
			conn2.Close()
		}
	}
	return res, err
}

//获取队列长度
func (r *RedisMgr) Llen(key string) (int, error) {
	conn := r.GetConn()
	if conn.Err() != nil {
		return 0, conn.Err()
	}
	defer func() {
		conn.Close()
	}()

	len, err := redis.Int(conn.Do("LLEN", key))
	if err != nil {
		conn2, _ := r.RetyCreateTempConn(err)
		if conn2 != nil {
			len, err = redis.Int(conn2.Do("LLEN", key))
			conn2.Close()
		}
	}
	return len, nil
}

// 判断一个key集合里是否存在某个value值，存在返回True
func (r *RedisMgr) Scontains(key string, data interface{}) (bool, error) {
	conn := r.GetConn()
	if conn.Err() != nil {
		return false, conn.Err()
	}
	defer func() {
		conn.Close()
	}()

	res, err := redis.Bool(conn.Do("SISMEMBER", key, data))
	if err != nil {
		conn2, _ := r.RetyCreateTempConn(err)
		if conn2 != nil {
			res, err = redis.Bool(conn2.Do("SISMEMBER", key, data))
			conn2.Close()
		}
	}
	return res, err
}

//向集合添加元素
func (r *RedisMgr) Sadd(key string, data interface{}) error {
	conn := r.GetConn()
	if conn.Err() != nil {
		return conn.Err()
	}

	defer func() {
		conn.Flush()
		conn.Close()
	}()

	err := conn.Send("SADD", key, data)
	if err != nil {
		conn2, _ := r.RetyCreateTempConn(err)
		if conn2 != nil {
			err = conn2.Send("SADD", key, data)
			conn2.Flush()
			conn2.Close()
		}
	}
	return err
}

//返回key集合所有的元素
func (r *RedisMgr) Smembers(key string) (interface{}, error) {
	conn := r.GetConn()
	if conn.Err() != nil {
		return nil, conn.Err()
	}

	defer func() {
		conn.Close()
	}()
	res, err := conn.Do("SMEMBERS", key)
	if err != nil {
		conn2, _ := r.RetyCreateTempConn(err)
		if conn2 != nil {
			res, err = conn2.Do("SMEMBERS", key)
			conn2.Close()
		}
	}
	return res, err
}

//在key集合中移除指定的元素
func (r *RedisMgr) Srem(key string, data interface{}) error {
	conn := r.GetConn()
	if conn.Err() != nil {
		return conn.Err()
	}

	defer func() {
		conn.Flush()
		conn.Close()
	}()

	err := conn.Send("SREM", key, data)
	if err != nil {
		conn2, _ := r.RetyCreateTempConn(err)
		if conn2 != nil {
			err = conn2.Send("SREM", key, data)
			conn2.Flush()
			conn2.Close()
		}
	}
	return err
}

//删除指定的key
func (r *RedisMgr) Clear(key string) error {
	conn := r.GetConn()
	if conn.Err() != nil {
		return conn.Err()
	}

	defer func() {
		conn.Flush()
		conn.Close()
	}()

	err := conn.Send("DEL", key)
	if err != nil {
		conn2, _ := r.RetyCreateTempConn(err)
		if conn2 != nil {
			err = conn2.Send("DEL", key)
			conn2.Flush()
			conn2.Close()
		}
	}
	return err
}

//设置数据
func (r *RedisMgr) Set(key string, data interface{}, expTime ...int64) error {
	conn := r.GetConn()
	if conn.Err() != nil {
		return conn.Err()
	}

	defer func() {
		conn.Flush()
		conn.Close()
	}()

	if len(expTime) > 0 {
		err := conn.Send("SET", key, data, "EX", expTime[0])

		if err != nil {
			conn2, _ := r.RetyCreateTempConn(err)
			if conn2 != nil {
				err = conn2.Send("SET", key, data, "EX", expTime[0])
				conn2.Flush()
				conn2.Close()
			}
		}

		return err
	} else {
		err := conn.Send("SET", key, data)

		if err != nil {
			conn2, _ := r.RetyCreateTempConn(err)
			if conn2 != nil {
				err = conn2.Send("SET", key, data)
				conn2.Flush()
				conn2.Close()
			}
		}
		return err
	}
}

//获取数据
func (r *RedisMgr) Get(key string) (interface{}, error) {
	conn := r.GetConn()
	if conn.Err() != nil {
		return nil, conn.Err()
	}

	defer func() {
		conn.Close()
	}()

	res, err := conn.Do("GET", key)

	if err != nil {
		conn2, _ := r.RetyCreateTempConn(err)
		if conn2 != nil {
			res, err = conn2.Do("GET", key)
			conn2.Close()
		}
	}
	return res, err

}

//设置某个hashKey名称的下的keyvalue值
func (r *RedisMgr) Hset(hashKey string, key string, data interface{}) error {
	conn := r.GetConn()
	if conn.Err() != nil {
		return conn.Err()
	}

	defer func() {
		conn.Flush()
		conn.Close()
	}()

	err := conn.Send("HSET", hashKey, key, data)
	if err != nil {
		conn2, _ := r.RetyCreateTempConn(err)
		if conn2 != nil {
			err = conn2.Send("HSET", hashKey, key, data)
			conn2.Flush()
			conn2.Close()
		}
	}
	return err
}

//得到某个hashKey名称下的key信息
func (r *RedisMgr) Hget(hashKey string, key string) (interface{}, error) {
	conn := r.GetConn()
	if conn.Err() != nil {
		return nil, conn.Err()
	}

	defer func() {
		conn.Close()
	}()

	res, err := conn.Do("HGET", hashKey, key)

	if err != nil {
		conn2, _ := r.RetyCreateTempConn(err)
		if conn2 != nil {
			res, err = conn2.Do("HGET", hashKey, key)
			conn2.Close()
		}
	}

	return res, err
}

//删除haskKey下面的key建
func (r *RedisMgr) Hdel(hashKey string, key string) error {
	conn := r.GetConn()
	if conn.Err() != nil {
		return conn.Err()
	}

	defer func() {
		conn.Flush()
		conn.Close()
	}()

	err := conn.Send("HDEL", hashKey, key)
	if err != nil {
		conn2, _ := r.RetyCreateTempConn(err)
		if conn2 != nil {
			err = conn2.Send("HDEL", hashKey, key)
			conn2.Flush()
			conn2.Close()
		}
	}
	return err
}

//获取hashKey的长度
func (r *RedisMgr) Hlen(hashKey string) (int, error) {
	conn := r.GetConn()
	if conn.Err() != nil {
		return 0, conn.Err()
	}

	defer func() {
		conn.Close()
	}()

	lr, err := redis.Int(conn.Do("HLEN", hashKey))
	if err != nil {
		conn2, _ := r.RetyCreateTempConn(err)
		if conn2 != nil {
			lr, err = redis.Int(conn2.Do("HLEN", hashKey))
			conn2.Close()
		}
	}
	return lr, err
}

//给hashKey里面指定的key建增加incrNum
//incrNum 必须为数字型
func (r *RedisMgr) Hincrby(hashKey string, key string, incrNum interface{}) error {
	switch incrNum.(type) {
	case int32, int, int64, int8, int16, float64, float32:
	default:
		return errors.New("参数incrNum必须为数字类型")
	}

	conn := r.GetConn()
	if conn.Err() != nil {
		return conn.Err()
	}

	defer func() {
		conn.Flush()
		conn.Close()
	}()

	err := conn.Send("HINCRBY", hashKey, key, incrNum)

	if err != nil {
		conn2, _ := r.RetyCreateTempConn(err)
		if conn2 != nil {
			err = conn2.Send("HINCRBY", hashKey, key, incrNum)
			conn2.Flush()
			conn2.Close()
		}
	}

	return err

}

//给指定的key增加num
//num 必须为数字型
func (r *RedisMgr) Incrnum(key string, num interface{}) error {
	switch num.(type) {
	case int32, int, int64, int8, int16, float64, float32:
	default:
		return errors.New("参数num必须为数字类型")
	}

	conn := r.GetConn()
	if conn.Err() != nil {
		return conn.Err()
	}

	defer func() {
		conn.Flush()
		conn.Close()
	}()

	err := conn.Send("INCRBY", key, num)
	if err != nil {
		conn2, _ := r.RetyCreateTempConn(err)
		if conn2 != nil {
			err = conn2.Send("INCRBY", key, num)
			conn2.Flush()
			conn2.Close()
		}
	}
	return err

}

// 设置有序集合
func (r *RedisMgr) Zset(key string, score interface{}, member string) error {
	conn := r.GetConn()
	if conn.Err() != nil {
		return conn.Err()
	}

	defer func() {
		conn.Flush()
		conn.Close()
	}()
	err := conn.Send("ZADD", key, score, member)
	if err != nil {
		conn2, _ := r.RetyCreateTempConn(err)
		if conn2 != nil {
			err = conn2.Send("ZADD", key, score, member)
			conn2.Flush()
			conn2.Close()
		}
	}
	return err

}

//获取有序集合的数据
func (r *RedisMgr) Zrange(key string, start int, end int, desc string, withScores bool) (res interface{}, err error) {
	conn := r.GetConn()
	if conn.Err() != nil {
		return nil, conn.Err()
	}

	defer func() {
		conn.Close()
	}()

	if strings.ToLower(desc) == "asc" {
		if withScores {
			res, err = conn.Do("ZRANGE", key, start, end, "WITHSCORES")
		} else {
			res, err = conn.Do("ZRANGE", key, start, end)
		}
	} else {
		if withScores {
			res, err = conn.Do("ZREVRANGE", key, start, end, "WITHSCORES")
		} else {
			res, err = conn.Do("ZREVRANGE", key, start, end)
		}
	}

	if err != nil {
		conn2, _ := r.RetyCreateTempConn(err)
		if conn2 != nil {
			if strings.ToLower(desc) == "asc" {
				if withScores {
					res, err = conn2.Do("ZRANGE", key, start, end, "WITHSCORES")
				} else {
					res, err = conn2.Do("ZRANGE", key, start, end)
				}
			} else {
				if withScores {
					res, err = conn2.Do("ZREVRANGE", key, start, end, "WITHSCORES")
				} else {
					res, err = conn2.Do("ZREVRANGE", key, start, end)
				}
			}

			conn2.Flush()
			conn2.Close()
		}
	}

	return
}

//删除有序集合key里面的member成员
func (r *RedisMgr) Zdel(key string, member string) error {
	conn := r.GetConn()
	if conn.Err() != nil {
		return conn.Err()
	}

	defer func() {
		conn.Flush()
		conn.Close()
	}()

	err := conn.Send("ZREM", key, member)
	if err != nil {
		conn2, _ := r.RetyCreateTempConn(err)
		if conn2 != nil {
			err = conn2.Send("ZREM", key, member)
			conn2.Flush()
			conn2.Close()
		}
	}
	return err
}

//计算有序集合在指定分数范围内的长度
func (r *RedisMgr) Zcount(key string, minSorce int, maxSorce int) (int, error) {
	conn := r.GetConn()
	if conn.Err() != nil {
		return 0, conn.Err()
	}

	defer func() {
		conn.Close()
	}()

	num, err := redis.Int(conn.Do("ZCOUNT", key, minSorce, maxSorce))
	if err != nil {
		conn2, _ := r.RetyCreateTempConn(err)
		if conn2 != nil {
			num, err = redis.Int(conn2.Do("ZCOUNT", key, minSorce, maxSorce))
			conn2.Close()
		}
	}
	return num, err
}

//获取某个分数段的集合
func (r *RedisMgr) ZrangeByScore(key string, minSorce int, maxSorce int) (interface{}, error) {
	conn := r.GetConn()
	if conn.Err() != nil {
		return nil, conn.Err()
	}

	defer func() {
		conn.Close()
	}()

	res, err := conn.Do("ZRANGEBYSCORE", minSorce, maxSorce)
	if err != nil {
		conn2, _ := r.RetyCreateTempConn(err)
		if conn2 != nil {
			res, err = conn2.Do("ZRANGEBYSCORE", minSorce, maxSorce)
			conn2.Close()
		}
	}
	return res, err
}

//获取成员member在有序集合key里面的排名
func (r *RedisMgr) Zrank(key string, member string, sort string) (num int, err error) {
	conn := r.GetConn()
	if conn.Err() != nil {
		return 0, conn.Err()
	}

	defer func() {
		conn.Close()
	}()

	if strings.ToLower(sort) == "asc" {
		num, err = redis.Int(conn.Do("ZRANK", key, member))
	} else {
		num, err = redis.Int(conn.Do("ZREVRANK", key, member))
	}

	if err != nil {
		conn2, _ := r.RetyCreateTempConn(err)
		if conn2 != nil {
			if strings.ToLower(sort) == "asc" {
				num, err = redis.Int(conn2.Do("ZRANK", key, member))
			} else {
				num, err = redis.Int(conn2.Do("ZREVRANK", key, member))
			}
			conn2.Close()
		}
	}

	return
}

//关闭所有连接
func (r *RedisMgr) Close() {
	r.pool.Close()
}

//检查是否是链接断掉的错误
func (r *RedisMgr) IsConnError(err error) bool {
	var needNewConn bool

	if err == nil {
		return false
	}

	if err == io.EOF {
		needNewConn = true
	}
	if strings.Contains(strings.ToLower(err.Error()), "use of closed network connection") {
		needNewConn = true
	}
	if strings.Contains(strings.ToLower(err.Error()), "connect: connection refused") {
		needNewConn = true
	}
	return needNewConn
}

//尝试生成新的临时链接
func (r *RedisMgr) RetyCreateTempConn(err error) (rconn redis.Conn, err2 error) {
	rconn = nil
	if r.IsConnError(err) != true {
		return nil, err
	}

	//重新建立链接 - 最大尝试次数
	maxRetry := 3
	for i := 0; i < maxRetry; i++ {
		if rconn == nil && i+1 > maxRetry {
			return nil, err
		}

		if rconn == nil {
			rconn, err = r.pool.Dial()
		}
		if err != nil {
			continue
		}
	}

	return rconn, err
}
